#include "jsettings.h"
#include <QSettings>

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
# include <QTextCodec>
#endif //qt6

#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX)
# include <QTECore/jasynchronization.h>
#endif

namespace QTE
{

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
static QTextCodec *gTextCodec = nullptr;
#endif //qt6

#define DEFAULT_FILE   "./QTE_Config.ini"

#define DEFAULT_GROUP  "DEFAULT"

static QString gFileName = DEFAULT_FILE;

static bool gReadFromFile = false;

#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX)
static JAsynchronization gAsync(new QThreadPool());
#endif

typedef QHash<QString, QVariant>  ConfigValue;

typedef QHash<QString, ConfigValue>  ConfigGroup;

static ConfigGroup gConfigGroup;

static QMutex gCGMutex;

static int __readConfig()
{
	gConfigGroup.clear();
	QSettings settings(gFileName, QSettings::IniFormat);

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
	if( gTextCodec != nullptr )
		settings.setIniCodec(gTextCodec);
#endif //qt6

	auto groupList = settings.childGroups();

	for(auto &group : groupList)
	{
		settings.beginGroup(group);
		auto it = gConfigGroup.insert(group, ConfigValue());

		for(auto &key : settings.childKeys())
			it->insert(key, settings.value(key));

		settings.endGroup();
	}
	return 0;
}

static __attribute_used__ int __read__ = __readConfig();

/*-------------------------------------------------------------------------------------------------*/

class Q_DECL_HIDDEN JSettingsPrivate
{
public:
	QString m_group = DEFAULT_GROUP;
};

/*-------------------------------------------------------------------------------------------------*/

JSettings::JSettings(const QString &group, QObject *parent) :
	QObject(parent),
	d_ptr(new JSettingsPrivate())
{
	setGroup(group);
}

JSettings::~JSettings()
{
	delete d_ptr;
}

void JSettings::setGroup(const QString &group)
{
	if( group.isEmpty() )
		d_ptr->m_group = DEFAULT_GROUP;

	d_ptr->m_group = group;
}

QString JSettings::group() const
{
	return d_ptr->m_group;
}

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
# define SETTINGS() \
	QSettings settings(gFileName, QSettings::IniFormat); \
	if( gTextCodec != nullptr ) \
		settings.setIniCodec(gTextCodec); \
	settings.beginGroup(d_ptr->m_group);
#else
# define SETTINGS() \
	QSettings settings(gFileName, QSettings::IniFormat); \
	settings.beginGroup(d_ptr->m_group);
#endif

void JSettings::write(const QString &key, const QVariant &value)
{
	gCGMutex.lock();
	auto gIter = gConfigGroup.find(d_ptr->m_group);

	if( gIter == gConfigGroup.end() )
		gIter = gConfigGroup.insert(d_ptr->m_group, ConfigValue());

	auto previous = gIter->value(key);
	gIter.value()[key] = value;
	gCGMutex.unlock();

	SETTINGS()
	settings.setValue(key, value);

	settings.endGroup();
	settings.sync();

#if defined(Q_OS_UNIX) || defined(Q_OS_LINUX)
	gAsync.run(std::bind(system, "sync"));
#endif

	emit written(key, value, previous);
}

QVariant JSettings::read(const QString &key, const QVariant &defaultValue)
{
	if( not gReadFromFile )
	{
		J_QMUTEX_LOCKER(gCGMutex);
		auto gIter = gConfigGroup.find(d_ptr->m_group);

		if( gIter == gConfigGroup.end() )
			return defaultValue;

		auto vIter = gIter->find(key);
		if( vIter == gIter->end() )
			return defaultValue;

		return *vIter;
	}

	SETTINGS()
	return settings.value(key, defaultValue);
}

void JSettings::setReadFromFile(bool enable)
{
	gReadFromFile = enable;
}

bool JSettings::isReadFromFile() const
{
	return gReadFromFile;
}

void JSettings::setFileName(const QString &fileName)
{
	if( gFileName.isEmpty() )
		gFileName = DEFAULT_FILE;

	gFileName = fileName;
	gCGMutex.lock();

	__readConfig();
	gCGMutex.unlock();
}

QString JSettings::fileName()
{
	return gFileName;
}

#ifndef __NO_TEXTCODEC

void JSettings::setIniCodec(QTextCodec *codec)
{
	gTextCodec = codec;
}

void JSettings::setIniCodec(const char *codecName)
{
	gTextCodec = QTextCodec::codecForName(codecName);
}

QTextCodec *JSettings::iniCodec()
{
	return gTextCodec;
}

#endif

} //namespace QTE
